package com.acme.bankapp.service.bank;

import com.acme.bankapp.domain.bank.*;
import com.acme.bankapp.exception.bank.ClientExistsException;
import com.acme.bankapp.exception.bank.FeedFormatException;

import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Feeder service, creating bank from feed file.
 * Every string in file looks like this:
 * accounttype=c;balance=100;overdraft=50;name=John;gender=f;
 * @author Gregory Sobko
 */
public class BankFeeder {

    private static final Pattern pattern = Pattern.compile(
                    "^accounttype=(?<accountType>c|s);" +
                    "balance=(?<balance>\\d+\\.?\\d*);" +
                    "(?:overdraft=(?<overdraft>\\d+\\.?\\d*);)?" +
                    "name=(?<name>[a-zA-Z0-9_\\s]+);" +
                    "gender=(?<gender>(m|f));$");

    private static String format = "accounttype=%s;balance=%f;overdraft=%f;name=%s;gender=%s;";

    private static final Charset charset = Charset.forName("UTF-8");


    /**
     * @param feedFile Path to feed file
     * @return Bank, created from feedFile
     * @throws ClientExistsException Duplicate clients in feed file
     * @throws FeedFormatException Malformed feed file
     */
    public static Bank getBankFromFeedFile(String feedFile) throws IOException, ClientExistsException, FeedFormatException {
        return getBankFromFeedFile(new File(feedFile));
    }

    /**
     * @param feedFile Feed file
     * @return Bank, created from feedFile
     * @throws ClientExistsException Duplicate clients in feed file
     * @throws FeedFormatException Malformed feed file
     */
    public static Bank getBankFromFeedFile(File feedFile) throws IOException, ClientExistsException, FeedFormatException {
        try(InputStream is = new FileInputStream(feedFile)){
            return getBankFromFeedStream(is);
        }
    }

    /**
     * @param inputStream input stream
     * @return Bank, created from feed stream
     * @throws ClientExistsException Duplicate clients in feed file
     * @throws FeedFormatException Malformed feed file
     */
    public static Bank getBankFromFeedStream(InputStream inputStream) throws IOException, ClientExistsException, FeedFormatException {
        Bank bank = new Bank();
        try(InputStreamReader inputStreamReader = new InputStreamReader(inputStream, BankFeeder.charset);
            BufferedReader br = new BufferedReader(inputStreamReader)){
            String line;
            while (( line = br.readLine()) != null) {
                bank.addClient(parseClientFromLine(line));
            }
        }
        return bank;
    }

    public static Client parseClientFromLine(String line) throws FeedFormatException {

        Matcher matcher = pattern.matcher(line);
        if(matcher.matches()){
            Client client = new Client(
                    matcher.group("name"),
                    matcher.group("gender").equals("m") ? Client.Gender.MALE: Client.Gender.FEMALE
            );
            Account account;
            switch (matcher.group("accountType")){
                case "c":
                    account = new CheckingAccount(
                            Double.parseDouble(matcher.group("balance")),
                            Double.parseDouble(matcher.group("overdraft"))
                            );
                break;
                case "s":
                default:
                    account = new SavingAccount(
                            Double.parseDouble(matcher.group("balance"))
                    );
                break;
            }
            client.setAccount(account);
            return client;
        }else{
            throw new FeedFormatException("Invalid input given!");
        }
    }

    public static String putClientToFeedLine(Client client) throws FeedFormatException{
        Account account = client.getAccount();
        if(account == null){
            throw new FeedFormatException("Cannot convert null account to feed string!");
        }
        return String.
                format(BankFeeder.format,
                        account instanceof CheckingAccount?"c":"s",
                        account.getBalance(),
                        account instanceof CheckingAccount? ((CheckingAccount) account).getOverdraftLimit() : 0,
                        client.getName(),
                        client.getGender() == Client.Gender.MALE ? "m" : "f"
                        );
    }
}
